﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Remoting.Messaging;
using System.Web;
using System.Diagnostics.Contracts;
using Microsoft.Practices.Unity;

namespace YasharEl.Infrastructure.ObjectFactory.Unity.LifeStyle
{
    public sealed class PerModuleExecutionContextLifetimeManager : LifetimeManager
    {
        #region Nested

        /// <summary>
        /// Custom extension for OperationContext scope
        /// </summary>
        class ModuleContainerExtension : IExtension<OperationContext>
        {
            #region Members

            public object Value { get; private set; }
            public string ModuleName { get; private set; }

            #endregion

            #region Constructors

            public ModuleContainerExtension(string moduleName, object value)
            {
                ModuleName = moduleName;
                Value = value;
            }

            #endregion

            #region IExtension<OperationContext> Members

            public void Attach(OperationContext owner)
            {

            }

            public void Detach(OperationContext owner)
            {

            }

            #endregion
        }

        #endregion

        #region Fields

        private Guid _key;
        private string _moduleName;

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public PerModuleExecutionContextLifetimeManager(string moduleName) : this(moduleName, Guid.NewGuid()) { }

        /// <summary>
        ///  Constructor
        /// </summary>
        /// <param name="key">A key for this lifetimemanager resolver</param>
        PerModuleExecutionContextLifetimeManager(string moduleName, Guid key)
        {
            Contract.Requires<ArgumentException>(key != null && key != Guid.Empty, "key could not be null.");
            Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(moduleName), "moduleName could not be null.");

            _key = key;
            _moduleName = moduleName;
        }

        #endregion

        #region ILifetimeManager Members

        /// <summary>
        /// <see cref="M:Microsoft.Practices.Unity.LifetimeManager.GetValue"/>
        /// </summary>
        /// <returns><see cref="M:Microsoft.Practices.Unity.LifetimeManager.GetValue"/></returns>
        public override object GetValue()
        {
            object result = null;

            //Get object depending on execution environment ( WCF without HttpContext, HttpContext or CallContext)
            if (OperationContext.Current != null)
            {
                //WCF without HttpContext environment
                ModuleContainerExtension containerExtension = FindModuleContainer(OperationContext.Current.Extensions);
                if (containerExtension != null)
                {
                    result = containerExtension.Value;
                }
            }
            else if (HttpContext.Current != null)
            {
                //HttpContext avaiable ( ASP.NET ..)
                if (HttpContext.Current.Items[_moduleName + _key.ToString()] != null)
                    result = HttpContext.Current.Items[_moduleName + _key.ToString()];
            }
            else
            {
                //Not in WCF or ASP.NET Environment, UnitTesting, WinForms, WPF etc.
                result = CallContext.GetData(_moduleName + _key.ToString());
            }


            return result;
        }

        /// <summary>
        /// <see cref="M:Microsoft.Practices.Unity.LifetimeManager.RemoveValue"/>
        /// </summary>
        public override void RemoveValue()
        {
            if (OperationContext.Current != null)
            {
                //WCF without HttpContext environment
                ModuleContainerExtension containerExtension = FindModuleContainer(OperationContext.Current.Extensions);
                if (containerExtension != null)
                    OperationContext.Current.Extensions.Remove(containerExtension);

            }
            else if (HttpContext.Current != null)
            {
                //HttpContext avaiable ( ASP.NET ..)
                if (HttpContext.Current.Items[_moduleName + _key.ToString()] != null)
                    HttpContext.Current.Items[_moduleName + _key.ToString()] = null;
            }
            else
            {
                //Not in WCF or ASP.NET Environment, UnitTesting, WinForms, WPF etc.
                CallContext.FreeNamedDataSlot(_moduleName + _key.ToString());
            }
        }

        /// <summary>
        /// <see cref="M:Microsoft.Practices.Unity.LifetimeManager.SetValue"/>
        /// </summary>
        /// <param name="newValue"><see cref="M:Microsoft.Practices.Unity.LifetimeManager.SetValue"/></param>
        public override void SetValue(object newValue)
        {
            if (OperationContext.Current != null)
            {
                //WCF without HttpContext environment
                ModuleContainerExtension containerExtension = FindModuleContainer(OperationContext.Current.Extensions);
                if (containerExtension == null)
                {
                    containerExtension = new ModuleContainerExtension(_moduleName, newValue);

                    OperationContext.Current.Extensions.Add(containerExtension);
                }
            }
            else if (HttpContext.Current != null)
            {
                //HttpContext avaiable ( ASP.NET ..)
                if (HttpContext.Current.Items[_moduleName + _key.ToString()] == null)
                    HttpContext.Current.Items[_moduleName + _key.ToString()] = newValue;
            }
            else
            {
                //Not in WCF or ASP.NET Environment, UnitTesting, WinForms, WPF etc.
                CallContext.SetData(_moduleName + _key.ToString(), newValue);
            }
        }

        #endregion

        #region Private Methods

        private ModuleContainerExtension FindModuleContainer(IExtensionCollection<OperationContext> iExtensionCollection)
        {
            foreach (ModuleContainerExtension moduleContainer in iExtensionCollection.FindAll<ModuleContainerExtension>())
            {
                if (moduleContainer.ModuleName == _moduleName)
                {
                    return moduleContainer;
                }
            }

            return null;
        }

        #endregion
    }
}
